package example.util;
import java.nio.charset.StandardCharsets;

public class Base94 {
    private static final String CHARACTERS = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
    private static final int BASE = 94;

    public static String encode(byte[] data) {
        StringBuilder result = new StringBuilder();

        for (int i = 0; i < data.length; i += 3) {
            int val1 = data[i] & 0xFF;
            int val2 = (i + 1 < data.length) ? data[i + 1] & 0xFF : 0;
            int val3 = (i + 2 < data.length) ? data[i + 2] & 0xFF : 0;

            int index1 = val1 % BASE;
            int index2 = ((val1 / BASE) + (val2 % BASE)) % BASE;
            int index3 = ((val2 / BASE) + (val3 % BASE)) % BASE;
            int index4 = (val3 / BASE) % BASE;

            result.append(CHARACTERS.charAt(index1));
            result.append(CHARACTERS.charAt(index2));
            result.append(CHARACTERS.charAt(index3));
            result.append(CHARACTERS.charAt(index4));
        }

        return result.toString();
    }

    public static byte[] decode(String encodedData) {
        byte[] result = new byte[(int) Math.ceil(encodedData.length() * 3.0 / 4.0)];

        for (int i = 0, j = 0; i < encodedData.length(); i += 4, j += 3) {
            int index1 = CHARACTERS.indexOf(encodedData.charAt(i));
            int index2 = CHARACTERS.indexOf(encodedData.charAt(i + 1));
            int index3 = CHARACTERS.indexOf(encodedData.charAt(i + 2));
            int index4 = CHARACTERS.indexOf(encodedData.charAt(i + 3));

            int val1 = (index1 + (index2 * BASE) + (index3 * BASE * BASE)) % 256;
            int val2 = ((index2 / BASE) + (index3 % BASE) + (index4 * BASE)) % 256;

            result[j] = (byte) val1;
            if (j + 1 < result.length) {
                result[j + 1] = (byte) val2;
            }
        }

        return result;
    }

    public static void main(String[] args) {
        String originalData = "Hello, World!";
        byte[] originalBytes = originalData.getBytes(StandardCharsets.UTF_8);

        String encodedData = Base94.encode(originalBytes);
        byte[] decodedBytes = Base94.decode(encodedData);
        String decodedData = new String(decodedBytes, StandardCharsets.UTF_8);

        System.out.println("Original data: " + originalData);
        System.out.println("Encoded data: " + encodedData);
        System.out.println("Decoded data: " + decodedData);
    }
}
